//
//  Category底层实现.h
//  面试题
//
//  Created by Toj on 8/20/21.
//


#pragma mark - 分类底层调用的方法
// 分类的处理, 最后会把所有的方法合并acmlist
static void
attachCategories(Class cls, category_list *cats, bool flush_caches);
/** 把cmlist合并到原类mlist
 * addedLists: 添加的cmlist
 * addedCount: 添加的数量
 */
void attachLists(List* const * addedLists, uint32_t addedCount);
// 判断当前类mlist是否有值
bool hasArray();
array_t *array();
// 记录当前类mlist 有值
void setArray(array_t *array);

#pragma mark - 分类的底层实现
// 分类的处理, 最后会把所有的方法合并
static void
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
    if (!cats) return;
    if (PrintReplacedMethods) printReplacements(cls, cats);

    bool isMeta = cls->isMetaClass();

    // fixme rearrange to remove these intermediate allocations
    method_list_t **mlists = (method_list_t **)
        malloc(cats->count * sizeof(*mlists));
    property_list_t **proplists = (property_list_t **)
        malloc(cats->count * sizeof(*proplists));
    protocol_list_t **protolists = (protocol_list_t **)
        malloc(cats->count * sizeof(*protolists));

    // Count backwards through cats to get newest categories first
    int mcount = 0;
    int propcount = 0;
    int protocount = 0;
    int i = cats->count;
    bool fromBundle = NO;
    while (i--) {
        auto& entry = cats->list[i];

        method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
        if (mlist) {
            mlists[mcount++] = mlist;
            fromBundle |= entry.hi->isBundle();
        }

        property_list_t *proplist =
            entry.cat->propertiesForMeta(isMeta, entry.hi);
        if (proplist) {
            proplists[propcount++] = proplist;
        }

        protocol_list_t *protolist = entry.cat->protocols;
        if (protolist) {
            protolists[protocount++] = protolist;
        }
    }

    auto rw = cls->data();

    prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
    rw->methods.attachLists(mlists, mcount);
    free(mlists);
    if (flush_caches  &&  mcount > 0) flushCaches(cls);

    rw->properties.attachLists(proplists, propcount);
    free(proplists);

    rw->protocols.attachLists(protolists, protocount);
    free(protolists);
}
// 分类的处理, 最后会把所有的方法合并
static void
attachCategories(Class cls, category_list *cats, bool flush_caches)
/** 把分类的方法合并到原类里
 * addedLists: 添加的list
 * addedCount: 添加的数量
 */
void attachLists(List* const * addedLists, uint32_t addedCount) {
    if (addedCount == 0) return;

    // 数组有很多值
    if (hasArray()) {
        // many lists -> many lists
        
        // 旧数组的数量
        uint32_t oldCount = array()->count;
        // 新数组数量 = 旧 + 添加
        uint32_t newCount = oldCount + addedCount;
        // 重新分配内存空间, 动态分配内存
        setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
        // 重新设置数组的数量
        array()->count = newCount;
        // 拷贝字节, 如果目标区域和源区域有重叠的话,
        // memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,
        // 但复制后源内容会被更改.
        // 但是当目标区域与源区域没有重叠则和memcpy函数功能相同
        /// 简单点就是现有的list往后移动addedCount位
        memmove(array()->lists + addedCount, array()->lists,
                oldCount * sizeof(array()->lists[0]));
        // memcpy指的是C和C++使用的内存拷贝函数,
        // 函数原型为void *memcpy(void *destin, void *source, unsigned n)
        // 函数的功能是从源内存地址的起始位置开始拷贝若干个字节到目标内存地址中,
        // 即从源source中拷贝n个字节到目标destin中
        /// 简单点就是把新的addedLists复制到array->lists内
        memcpy(array()->lists, addedLists,
               addedCount * sizeof(array()->lists[0]));
    }
    else if (!list  &&  addedCount == 1) { // list为空 添加的数量为1
        // 0 lists -> 1 list
        list = addedLists[0];
    }
    else {
        // 这个没想明白什么时候会到这里, 但是添加方式跟上面1 类似
        // 1 list -> many lists
        List* oldList = list;
        uint32_t oldCount = oldList ? 1 : 0;
        uint32_t newCount = oldCount + addedCount;
        setArray((array_t *)malloc(array_t::byteSize(newCount)));
        array()->count = newCount;
        if (oldList) array()->lists[addedCount] = oldList;
        memcpy(array()->lists, addedLists,
               addedCount * sizeof(array()->lists[0]));
    }
}

// 判断array是否有值
bool hasArray() const {
    return arrayAndFlag & 1;
}

array_t *array() {
    return (array_t *)(arrayAndFlag & ~1);
}

// 记录array 有值
void setArray(array_t *array) {
    arrayAndFlag = (uintptr_t)array | 1;
}
